unit BD_UUT_MTComms;

interface
uses vcl.grids, sysutils, CPort, system.Math, Vcl.StdCtrls, IDGlobal,
 Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.ExtCtrls, System.Classes,
 System.SyncObjs, BD_UUT, BD_PgmCalMT_Aux;


 //TThLJCom is a descendant of TThread which will be spawned to handle reading
//values from the LJ UART to free the main thread to update the GUI.
type
THLJCom = class(TThread)
  private
    Fcnt: Integer;         //used in procedures to receive byte count from LJ ReadUart.
    procedure EnterCal;    //cause UUT to enter calibration mode.
    procedure SetADPressure;    //Read AD Data from UUT.
    procedure CaptureAD;
    procedure SendSN;     //Send the caclulated SN to the UUT.
    procedure ReadSN;   //receive the stored SN from the UUT.
    procedure SendParams;  //send calibration paramters to the UUT.
    procedure ExitCal;    //Exit the calibration routine and power off.
    procedure DispUtil;   //LCD/LED test functions
    procedure ResetUUT;   //Reset the UUT and make sure it responds
    procedure ReadUUTPressure;    //read Sensotec pressure (also applied to UUT)
    procedure LeakTest;       //perform a leak test of the UUT
    procedure ReadBattV;     //Gets the battery voltage
    procedure UpdateStatus(status: boolean);    //save error information to shared memory area. True = pass;
  protected
    procedure Execute; override;
    function ExchangeData(bytesExp, respTime: Integer; TxD: array of byte): Integer; //exchange data with UUT via LJ UART.
  public
    FADVals: array of byte;    //holds the 27 bytes read from the UUT A/D.
    FADValsAvg: array of Integer;   //collects FADCnt reads for averaging.
    FADCnt: Integer;       //number of times to read the A-D
    FTxCmd: array of byte;  //Command to send.
    FRxExpBytes: Integer;    //number of bytes expected in response from UUT
    FRxTime: Integer;      //max time (in mS) for the UUT to respond to a command
    FRxBuf: TByteArr;      //buffer with accumulated bytes fromUUT are stored during xfer.
    FCalSeq: TCalSequence;   //Field for local tracking of calibration sequence.
    FAutoCal: Boolean;   //flag to indicate if autocal is on.
    FReady: Boolean;    //flag for tracking of UUT status while in calibration thread.
    FADTestDone: Boolean; //flag to track that all 3 A/D pressures have been tested
    FAppliedPress: double;   //pressure applied to the UUT
    FBattMode: TBattMeas;   //tracks the battery measurment currently being made
    FLeakTestOK: Boolean;   //tracks leaktest status as it's running.
    constructor Create(seq: TCalSequence; autoCal: boolean); overload;
    constructor Create(seq: TCalSequence; ADCnt: Integer); overload;
    constructor Create(seq: TCalSequence; BattMode: TBattMeas); overload;
end;

implementation
uses BDPgmCalMTMain, BD_LabJack, BD_TestHead;
(*******************************************************************************
 TThLJCom, Member functions, constructor }
********************************************************************************)
constructor THLJCom.Create(seq: TCalSequence; autoCal: boolean);
begin
  FCalSeq := seq;          //save the current cal sequence.
  FAutoCal := autoCal;
  inherited Create(False);   //do not want to create suspended.
end;
//Overloaded version for A/D comms
constructor THLJCom.Create(seq: TCalSequence; ADCnt: Integer);
begin
  FCalSeq := seq;
  FAutoCal := False;
  FADCnt := ADCnt;
  inherited Create(False);   //do not want to create suspended.
end;
//Overloaded constructor for Battery Voltage Measurement
constructor THLJCom.Create(seq: TCalSequence; BattMode: TBattMeas);
begin
  FCalSeq := seq;          //save the current cal sequence.
  FBattMode := BattMode;
  inherited Create(False);   //do not want to create suspended.
end;

{*******************************************************************************
  THLJcom Execute: Entry point of thread
********************************************************************************}
procedure THLJCom.Execute;
begin
  FreeOnTerminate := True;    //thread will teminate once UUT data exchange is complete
  case FCalSeq of
    csReset: ResetUUT;
    csEnterCal:  EnterCal;
    csCaptureZero,
    csCaptureMax,
    csCaptureRTZ,
    csCaptureOnce: SetADPressure;
    csSendSN: SendSN;
    csReadSN: ReadSN;
    csSendParams: SendParams;
    csExitCal: ExitCal;
    csLcdOn,
    csLcdOff,
    csLedOn,
    csLedOff: DispUtil;
    csLeakTest: LeakTest;
    csBattV: ReadBattV;
  end;
end;

{*******************************************************************************
 THLJcom ExchangeData: Exchanges bytes of data with UUT using LJ UART.
********************************************************************************}
function THLJCom.ExchangeData(bytesExp, respTime: Integer; TxD: array of byte): Integer;
var
  i, idx, j, RxCnt, RxTot: Integer; str: string; buf: TByteArr;
begin
{$IFDEF SHOWTX}
  for i := Low(TxD) to High(TxD) do
    str := str + TxD[i].ToHexString(2);
  i := Length(TxD);
  Synchronize(
    procedure
      begin
         frmMain.Memo1.Lines.Add('TX-> ['+i.ToString +'] '+ str);
      end);
{$ENDIF}
  LJ.FlushUART;
  LJ.WriteUART(TxD);   //send the command to the LJ UART.
  SetLength(buf, bytesExp);
  SetLength(FRxBuf, 0);    //clear the current data
  i := 0;
  idx := 0;
  while ((i < respTime) and (idx < (bytesExp))) do
    begin
      sleep(respTime div 10);    //wait to allow UUT time to respond.
      RxCnt := LJ.ReadUART2(buf[idx]);
      FRxBuf := FRxBuf + buf; //Copy(buf,Low(buf),Length(buf)), new auto-length assignment under XE7.
      idx := idx + RxCnt;
      i:=I+(respTime div 10);   //must inc while counter by respTime/10 due to respTime mS sleep between reads of UART.
    end;   //end while
  respTime := i;   //save for dispaly
  //now see if all of the bytes expected were received.
{$IFDEF SHOWTX}
  str := '';
  for i := Low(FRxBuf) to High(FRxBuf) do
    str := str + ', ' + FRxBuf[i].ToHexString(2);
  Synchronize(
    procedure
      begin
         frmMain.Memo1.Lines.Add('RX<- ['+idx.ToString+'] '+ str + #13 +
                                 'respTime: '+ respTime.ToString);
         frmMain.ScrollToBottom;
      end);
{$ENDIF}
  Result := idx;     //return the total number of bytes in RxBuf
end;

(*******************************************************************************
 ResetUUT: Issues reset to the UUT.
*******************************************************************************)
procedure THLJCom.ResetUUT;
var
  buf: TByteArr;
begin
  FCalSeq := csReset;   //set at start of every procedure as AutoCal will not set
  LJ.FlushUART;  //Flush UART to make sure it's empty
  LJ.PutBit(6,0);
  sleep(20);
  LJ.PutBit(6,1);  //Toggle reset
  //At this point, the UUT should send CAL_UUTACTIVE to the UART. Need to verify.
  sleep(500);     //Give the UUT a 500 mS to respond. This is a min requirment, so don't change.
  SetLength(buf, 1);
  if LJ.ReadUART2(buf)>0 then
     if buf[High(buf)] = CAL_UUTACTIVE then
       UpdateStatus(True)
       else
         UpdateStatus(False);
end;

(*******************************************************************************
 EnterCal: Command UUT to enter cal mode. Exchange PW with UUT.
*******************************************************************************)
procedure THLJCom.EnterCal;
begin
  FBattMode := bvStartCal;
  ReadBattV;
  Synchronize(procedure
    var i,j: Integer;
    begin
      frmMain.sbStatus.Panels[0].Text := 'Entering Calibration Mode';
      for i := 1 to frmMain.sgADData.ColCount-1  do
        for j := 1 to frmMain.sgADData.RowCount-1 do
          frmMain.sgADData.Cells[i,j] := '';
      for j := 1 to frmMain.sgSN.RowCount-1  do
        frmMain.sgSN.Cells[1,j] := '';
    end);
  ResetUUT;
  FCalSeq := csEnterCal;
  Fcnt := ExchangeData(1, TIM_ENTERCAL, [CAL_ENTERCAL1,CAL_ENTERCAL2]);    //Command UUT to enter Cal.
  if (FRxBuf[High(FRxBuf)] = CAL_READY) then
     Fcnt := ExchangeData(1, TIM_ENTERCAL, [CAL_PW1]);
  if (FRxBuf[High(FRxBuf)] = CAL_PW1) then
     Fcnt := ExchangeData(1, TIM_ENTERCAL, [CAL_PW2]);
  if (FRxBuf[High(FRxBuf)] = CAL_PW2) then
     Fcnt := ExchangeData(1, TIM_ENTERCAL, [CAL_PW3]);
  if (FRxBuf[High(FRxBuf)] = CAL_PW3) then
     Fcnt := ExchangeData(2, TIM_ENTERCAL, [CAL_PW4]);
  if Fcnt > 1 then
    begin
      if ((FRxBuf[High(FRxBuf)-1] = CAL_PW4) and
         (FRxBuf[High(FrxBuf)] = CAL_READY)) then
         begin
           UpdateStatus(True);
           if FAutoCal then
             //SendSN;
             begin
               UUT.NumReadsRequired := 15; //must init to prevent divide by zero error.
               UUT.ADCapture := adZero;   //init to adZero before call to CaptureAD.
               FADCnt := 15;    //must configure here due to Create() overload called
               FCalSeq := csCaptureZero;
               SetADPressure;
             end;
         end
         else
           UpdateStatus(False);
    end
      else
        UpdateStatus(False);
end;

(*******************************************************************************
  SetADPressure: Set the pressure to the UUT, and verify that it's in range.
*******************************************************************************)
procedure THLJCom.SetADPressure;
var
  i: Integer; msg: string; PressMax, PressMin: double;
begin
  Synchronize(procedure
    begin
      frmMain.cbFastest.Checked := True;
      case FCalSeq of
        csCaptureZero:
          begin
            PressMax := cfgParams.SystemPresZeroPresHighCnfg;
            PressMin := cfgParams.SystemPresZeroPresLowCnfg;
            frmMain.sbStatus.Panels[0].Text := 'Reading Zero System Pressure value.';
            frmMain.cbHighPress.Checked := False;
            frmMain.cbVent.Checked := False;
          end;
        csCaptureMax:
          begin
            PressMax := cfgParams.SystemPresFullPresHighCnfg;
            PressMin := cfgParams.SystemPresFullPresLowCnfg;
            frmMain.sbStatus.Panels[0].Text := 'Reading Full System Pressure value.';
            frmMain.cbHighPress.Checked := True;
            frmMain.cbVent.Checked := True;
          end;
        csCaptureRTZ:
          begin
            PressMax := cfgParams.SystemPresZeroPresHighCnfg;
            PressMin := cfgParams.SystemPresZeroPresLowCnfg;
            frmMain.sbStatus.Panels[0].Text := 'Reading RTZ System Pressure value.';
            frmMain.cbHighPress.Checked := False;
            frmMain.cbVent.Checked := False;
          end;
      end;
    end);
  Sleep(3000);    //allow pressure to drop
  i := 0;
  FAppliedPress := 10;  //sent to initial value to force loop to run.
  while((i<5) and (FAppliedPress < PressMin) or (FAppliedPress > PressMax)) do
    begin
      ReadUUTPressure;
      Sleep(1000);
      inc(i);
    end;
   Synchronize(procedure
      begin
        case FCalSeq of
           csCaptureZero: UUT.PresMeasZeroPres := FAppliedPress;
           csCaptureMax: UUT.PresMeasFullPres := FAppliedPress;
           csCaptureRTZ: UUT.PresMeasReturnZero := FAppliedPress;
        end;
        CheckSysPressure(FCalSeq);
        FReady := UUT.UUTReady;
      end);
  if FReady then      //Only allow to proceed if the pressure checked out within range.
    CaptureAD;
end;

(*******************************************************************************
  CaptureAD: Read AD values from UUT using LJ UART.
*******************************************************************************)
procedure THLJCom.CaptureAD;
var
  ADCycs: Integer; i: Integer; Rdy: Boolean; msg: string;
begin
  case FCalSeq of
    csCaptureZero: msg := 'Reading A/D at Zero pressure';
    csCaptureMax: msg :='Reading A/D at Full pressure';
    csCaptureRTZ: msg :='Reading A/D at RTZ pressure';
    csCaptureOnce: msg :='Reading A/D at applied pressure';
  end;
  ADCycs := 0;
  SetLength(FADValsAvg, 28);
  //clear out the buffer.
  for i := Low(FADValsAvg) to High(FADValsAvg) do
                FADValsAvg[i] := 0;
  //Now read the values from the UUT a total of ADCycs times.
  while ADCycs < FADCnt do
    begin
      Fcnt := ExchangeData(SZ_AD_VALS, TIM_SEND_AD, [CAL_SEND_AD]);    //Command to request A/D data from UUT.
      if (Fcnt = SZ_AD_VALS) and (FRxBuf[High(FRxBuf)] = CAL_READY) then   //verify complete data packet returned
        begin
          Rdy := True;
          for i := Low(FADValsAvg) to High(FADValsAvg) do    //add each successive read to the buffer.
                FADValsAvg[i] := FADValsAvg[i]+ FRxBuf[i];
        end
        else
          begin
            Rdy := False;
            UpdateStatus(False);
            Exit;          //exits the procedure.
          end;
      inc(ADCycs);
    end;   //end while loop
  if Rdy then
    begin
      Synchronize(procedure
         var j: Integer;
         begin
           frmMain.sbStatus.Panels[0].Text  := msg;
           //copy A/D values average to shared mem UUT array
           for j := Low(FADValsAvg) to High(FADValsAvg) do
             UUT.ADArray[j] := byte((FADValsAvg[j]div FADCnt));   //get avg &convert to byte
           UUT.CalSequence := FCalSeq;   //must synchronize for next line;
           frmMain.adCaptureVals;
           FReady := UUT.UUTReady;   //need to make sure data check didn't invalidate UUT ready status
         end);                       //before continuing if AutoCal is active.
     UpdateStatus(True);
     if (FAutoCal) and (FReady) then
       begin
         if FCalSeq < csCaptureRTZ then
           begin
             inc(FCalSeq);
             SetADPressure;
           end
           else
             SendParams;
       end;
    end;
end;

(*******************************************************************************
  SendSN: Send the computed serial number to the UUT.
*******************************************************************************)
procedure THLJCom.SendSN;
var
  tmp: Integer; sn: array[1..8] of byte;
begin
  FCalSeq := csSendSn;
  Synchronize(procedure
    var
      i: Integer;
    begin
      frmMain.sbStatus.Panels[0].Text := 'Sending SN to UUT.';
      UUT.BuildSN;
      frmMain.memo1.Lines.Add(
        'bSN[1]:' + IntToStr(byte(UUT.bSNsent[1])) + ', ' +
        'bSN[2]:' + IntToStr(byte(UUT.bSNsent[2])) + ', ' +
        'bSN[3]:' + IntToStr(byte(UUT.bSNsent[3])) + ', ' +
        'bSN[4]:' + IntToStr(byte(UUT.bSNsent[4])) + ', ' +
        'bSN[5]:' + IntToStr(byte(UUT.bSNsent[5])) + ', ' +
        'bSN[6]:' + IntToStr(byte(UUT.bSNsent[6])) + ', ' +
        'bSN[7]:' + IntToStr(byte(UUT.bSNsent[7])) + ', ' +
        'LSB of sum: ' + IntToStr(byte(UUT.bSum))
        );
      for i := 1 to 7 do
        sn[i] := UUT.bSNsent[i];    //copy the global array to local
      sn[8] := UUT.bSum;
    end);
  Fcnt := ExchangeData(1, TIM_STORE_SN, [CAL_STORE_SN]);
  if FRxBuf[High(FRxBuf)] = CAL_STORE_SN then
  begin
    tmp := 1;
    while tmp < 8 do
      begin
        Fcnt := ExchangeData(1, TIM_STORE_SN, sn[tmp]);
        if FRxBuf[High(FRxBuf)] = sn[tmp] then
          inc(tmp)
          else
            begin
              UpdateStatus(False);
              Exit;      //exit this procedure
            end;
      end;
  end;
  Fcnt := ExchangeData(2, TIM_STORE_SN, sn[8]);   //send checksum byte.
  if(Fcnt = 2) then
  if (FRxBuf[0] = sn[8]) and (FRxBuf[High(FRxBuf)] = CAL_READY)  then
    begin
      UpdateStatus(True);
      if FAutoCal then
        ReadSN;
    end
    else
      UpdateStatus(False);
end;

(*******************************************************************************
  ReadSN: Receive the stored serial number from the UUT.
*******************************************************************************)
procedure THLJCom.ReadSN;
var
  i: integer;
begin
  FCalSeq := csReadSn;
  Fcnt := ExchangeData(20, TIM_SEND_SN, [CAL_SEND_SN]);
  if (FRxBuf[Low(FRxBuf)] = CAL_SEND_SN) and (FRxBuf[High(FRxBuf)] = CAL_READY) then
    begin
      Synchronize(procedure
        var i: integer;
        begin
          frmMain.sbStatus.Panels[0].Text := 'Reading S/N from UUT';
          for i := 1 to 18 do                 //copy the data only to the UUT sn buffer.
            UUT.bSNRcv[i] := FRxBuf[i];
          UUT.ProcessSNData(frmMain.sgSN);
        end);
      UpdateStatus(True);
      if FAutoCal then
        (*begin
          UUT.NumReadsRequired := 15; //must init to prevent divide by zero error.
          UUT.ADCapture := adZero;   //init to adZero before call to CaptureAD.
          FADCnt := 15;    //must configure here due to Create() overload called
          FCalSeq := csCaptureZero;
          SetADPressure;
        end;*)
        ExitCal;
    end
    else
      UpdateStatus(False);
end;

(*******************************************************************************
SendParams: Send calculated calbration params to the UUT.
*******************************************************************************)
procedure THLJCom.SendParams;
var
  i: integer; str: string; Rdy: Boolean; buf: array [1..47] of byte;
begin
  //SetLength(buf, High(UUT.bParams));
  FBattMode := bvEndCal;  //read and save the final battery voltage before shutting down
  ReadBattV;
  FCalSeq := csSendParams;
  Synchronize(procedure     //need local copy of buffer to prevent thread lock.
    var
      i: Integer;
    begin   //copy the calcualted (and stored) parameters from the UUT object to the local buffer
      frmMain.sbStatus.Panels[0].Text := 'Sending params to UUT';
      UUT.CalcADVals;
      UpdateParamGrid;
      for i :=  Low(UUT.bParams) to High(UUT.bParams) do
        buf[i] := UUT.bParams[i];
    end);
  Fcnt := ExchangeData(SZ_SEND_PARAMS, TIM_SEND_PARAMS, [CAL_SEND_PARAMS]);
  if (Fcnt = SZ_SEND_PARAMS) and (FRxBuf[High(FRxBuf)] = CAL_SEND_PARAMS) then
    begin
    //send parameters a byte at at time to UUT & Verify each byte for correctness
     for i :=  Low(buf) to High(buf)  do
      begin
        if i < High(buf) then
          Fcnt := ExchangeData(SZ_SEND_PARAMS, TIM_SEND_PARAMS, [buf[i]])  //send byte
          else         //do this when final byte is reached.
            Fcnt := ExchangeData(SZ_SEND_PARAMS+1, TIM_SEND_PARAMS, [buf[i]]);
        //now check byte returned and see if it's echoed correctly.
        if (Fcnt = SZ_SEND_PARAMS) then
         begin
         if FRxBuf[High(FRxBuf)] = buf[i] then    //if bytes match
           begin
             str := str + '['+FRxBuf[High(FRxBuf)].ToHexString+'], ';
             Rdy := True;
           end
           else
             begin
               Rdy := False;
               UpdateStatus(False);
               Exit;
             end;
         end
         else
           //unique case for last byte, where UUT will respond with final byte + CAL_READY
           if (Fcnt = 2) and (FRxBuf[High(FRxBuf)] = CAL_READY) then  //if correct
             begin
               str := str + '['+FRxBuf[Low(FRxBuf)].ToHexString+']';
               Rdy := True;
             end
             else
               Rdy := False;
      end;
    end;
  if Rdy then
    begin
      UpdateStatus(True);
      if FAutoCal then
        //ExitCal;
        SendSN;
    end
    else
     UpdateStatus(False);
end;

(*******************************************************************************
 rxExitCal: Instruct the UUT to exit calibration mode and shut down.
*******************************************************************************)
procedure THLJCom.ExitCal;
var
  str: string; i: integer; CalOK: boolean;
begin
   FCalSeq := csExitCal;
   Fcnt := ExchangeData(3, TIM_EXIT, [CAL_EXIT]);
   for i := Low(FRxBuf) to Fcnt do
     str := str + '[' + FRxBuf[i].ToString()+'] ';
   if Fcnt > 2 then   //3 bytes expected for correct Exit Cal
     begin
       if (FRxBuf[High(FRxBuf)-1] = CAL_READY) and (FRxBuf[High(FRxBuf)] = CAL_READY) then
         UpdateStatus(True)
         else
           UpdateStatus(False);
     end
       else
         UpdateStatus(False);
end;

(*******************************************************************************
  DispUtil: Handle responsed from UUT when commanded to do LCD/LED display utils
*******************************************************************************)
procedure THLJCom.DispUtil;
begin
  case FCalSeq of
    csLedOn: Fcnt := ExchangeData(2, TIM_LCD, [CAL_LEDON]);
    csLedOff: Fcnt := ExchangeData(2, TIM_LCD, [CAL_LEDOFF]);
    csLcdOn: Fcnt := ExchangeData(2, TIM_LCD, [CAL_LCDON]);
    csLcdOff: Fcnt := ExchangeData(2, TIM_LCD, [CAL_LCDOFF]);
  end;
  if (Fcnt = 2) and (FRxBuf[High(FRxBuf)] = CAL_READY) then
    UpdateStatus(True)
    else
      UpdateStatus(False);
end;

(*******************************************************************************
 GetUUTPressure: Read the pressure from the Sensotec and Display.
 ******************************************************************************)
procedure THLJCom.ReadUUTPressure;
var
  tmp: Double; i: Integer;
begin
  FAppliedPress := 0;
  for i := 1 to 10 do     //note- 50 ms elapses everytime this is called.
    begin
      LJ.GetAnalogInput(1,tmp);
      FAppliedPress := FAppliedPress + tmp;
      Sleep(5);
    end;
  FAppliedPress := FAppliedPress/10;      //get average reading
  FAppliedPress := FAppliedPress*100;
  Synchronize(procedure
    begin
      UUT.PresCurrent := FAppliedPress;
      frmMain.lblPress.Caption := FloatToStrF(FAppliedPress, ffFixed, 15,1) +
                          ' PSI / ' + FloatToStrF(FAppliedPress/14.69595, ffFixed, 15, 1) +
                          ' ATM';
    end);
end;

(*******************************************************************************
 LeakTest: Pressurize the UUT and check for leaks (leak decay)
 ******************************************************************************)
 procedure THLJCom.LeakTest;
 var
  i, LeakCnt: Integer; StartPressure, Decay, MaxPress, MinPress, LeakHi, LeakLo: Double;
  timStart, timElapsed: LongWord;
begin
 FCalSeq := csLeakTest;
 Synchronize(procedure
   begin
     MaxPress := cfgParams.SystemPresFullPresHighCnfg;    //Get value from config file
     MinPress := MaxPress - 5;
     frmMain.cbFastest.Checked := True;
     frmMain.cbVent.Checked := True;
     Sleep(50);
     frmMain.cbHighPress.Checked := True;
     MemMsg('Pressuring UUT to ' + cfgParams.SystemPresFullPresHighCnfg.ToString(ffFixed,15,3) + ' PSI.');
     frmMain.sbStatus.Panels[0].Text := 'Performing Leak Test.';
   end);
 Sleep(5000);    //need 5 seconds to pressurize
  i := 0;
  FAppliedPress := 0;  //sent to initial value to force loop to run.
  while((i<5) and ((FAppliedPress >MaxPress) or (FAppliedPress < MinPress))) do
    begin
      ReadUUTPressure;
      Sleep(1000);
      inc(i);
    end;
  Synchronize(procedure
        begin
          UUT.PresCurrent := FAppliedPress;
          CheckLeakTest(lsStabilize);
          FLeakTestOK := UUT.LeakTestOK;
        end);
  if FLeakTestOK then  //IF the system obtained full pressure and stabilized
     //Set up the system to check pressure over 15 seconds and see if a leak occurs.
      begin
        StartPressure := FAppliedPress;    //record starting pressure
        timStart := Ticks;        //gets the miliseconds since the computer started
        Synchronize(procedure
          begin
            UUT.PressLeak1Meas := StartPressure;     //capture start pressure for log file.
            frmMain.Memo1.Lines.Add('Syringe successfully pressurized to ' + FloatToStrF(UUT.PresCurrent,ffFixed, 15,1) + 'PSI.');
            frmMain.Memo1.Lines.Add('Beginning 15 second leak decay test.');
            frmMain.cbSeal.Checked := True;   //Seal the pressure in the syring
            frmMain.cbHighPress.Checked := False; //Turn off High Pressure
            frmMain.cbVent.Checked := False;  //Release the high pressure from system
            frmMain.pbLeakTime.Position := 0;
            frmMain.lblDecayVal.Caption := '0.0 PSI';
            frmMain.pbLeakTime.Visible := True;
            frmMain.lblLeakRate.Visible := True;
            frmMain.lblLeakRate.Caption := FloatToStrF(FAppliedPress,ffFixed, 15,1);
            frmMain.lblDecayVal.Visible := True;
          end);
          LeakCnt := 1;     //initialize
        //Now check the pressure and update the GUI.
        timElapsed := GetTickDiff(timStart, Ticks);
        while timElapsed < CAL_LEAK_TIME_TOT do
          begin
            Sleep(CAL_LEAK_READ_PER);   //small Delay
            ReadUUTPressure;  //10 ms will elapse here, due to 1 ms delays between 10 reads.
            timElapsed := GetTickDiff(timStart, Ticks);
            if ((timElapsed) div LeakCnt) > 1000  then   //Step the leak test time progress bar every second
              begin
                inc(LeakCnt);
                Decay := ((UUT.PressLeak1Meas - UUT.PresCurrent)*1000) / timElapsed.ToDouble;
                Synchronize(procedure
                  begin
                    frmMain.pbLeakTime.StepIt;
                    try
                      frmMain.lblDecayVal.Caption := 'Decay Val: '+ Decay.ToString(ffFixed,15,3) +
                                                     ' PSI/Sec';
                    except
                      MemMsg('Failure during leak pressure conversion.');
                      FReady := False;
                      UUT.UUTReady := False;
                      frmMain.ConfigCalAccess;
                      Exit;
                    end;
                    UUT.PresCurrent := FAppliedPress;
                    UUT.ElapsedTime := timElapsed;   //record elapsed time since test started in mS
                    CheckLeakTest(lsDecay);
                    FLeakTestOK := UUT.LeakTestOK;
                  end);
              end;
             if FLeakTestOK then  //CheckLeakTest was good
               begin
                 if timElapsed >= CAL_LEAK_TIME_TOT then     //If the leak test required time has elapsed.
                  begin
                    Synchronize(procedure
                    begin
                      UUT.ElapsedTime := timElapsed;   //record elapsed time since test started in mS
                      UUT.PressLeak2Meas := FAppliedPress;  //record ending pressure.
                      UUT.PressLeakCalc := UUT.PressLeak1Meas- UUT.PressLeak2Meas;
                      CheckLeakTest(lsFinal);
                      frmMain.pbLeakTime.Visible := False;
                      frmMain.cbSeal.Checked := False;    //Release seal on syringe
                      frmMain.cbVent.Checked := False;    //Allow to vent
                      FLeakTestOK := UUT.LeakTestOK;
                      if FLeakTestOK then
                        begin
                          Decay := ((UUT.PressLeak1Meas - UUT.PresCurrent)*1000) / timElapsed.ToDouble;
                          MemMsg('Leak test passes. Leak rate: '+ Decay.ToString(ffFixed,15,3) +
                                 ' PSI/Sec');
                          MemMsg('Leak time total (mS): '+timElapsed.ToString);
                          frmMain.lblLeakRate.Visible := False;
                          frmMain.lblDecayVal.Visible := False;
                          frmMain.pbLeakTime.Visible := False;
                        end;
                    end);
                    if (FAutoCal)and (FLeakTestOK) then
                      EnterCal;
                  end;
               end
              else
                break;   //break the while loop if FReady is false.
          end;
      end;
end;

(*******************************************************************************
  ReadBattV: Read the Battery voltage and display on GUI. Takes 10 readings,
                calculates average, and displays to two decimal places.
*******************************************************************************)
procedure THLJCom.ReadBattV;
var
  tmp: Double; i: Integer; BattV: Double;
begin
  BattV := 0;
  for i := 1 to 10 do
    begin
      LJ.GetAnalogInput(ACH_BATT,tmp);
      BattV := BattV + tmp;
      Sleep(1);
    end;
  BattV := BattV/10;      //get average reading
  Synchronize(procedure
    begin
      frmMain.lblBattV.Visible := True;
      frmMain.lblBattV.Caption := FloatToStrF(BattV, ffFixed, 15,2) + ' V';
      //Capture the battery voltages depending on the mode for the log file.
      case FBattMode of
        bvPreProg: UUT.BatMeasPreProg := BattV;
        bvStartCal: UUT.BatMeasStartCal := BattV;
        bvEndCal: UUT.BatMeasEndCal := BattV ;
      end;
    end);
end;

(*******************************************************************************
 UpdateStatus: Reports on the success (or failure) of attempts to communicate
               calibration data to/from the UUT.
 ******************************************************************************)
procedure THLJCom.UpdateStatus(status: boolean);
var
  rec: TErrorRec;
begin
  rec.FSeq := FCalSeq;
  case FCalSeq of
    csReset: rec.FMethod := 'Reset';
    csEnterCal: rec.FMethod := 'Enter Calibration';
    csCaptureZero: rec.FMethod := 'Capture A/D Zero';
    csCaptureMax:  rec.FMethod := 'Capture A/D Max';
    csCaptureRTZ:  rec.FMethod := 'Capture A/D RTZ';
    csCaptureOnce: rec.FMethod := 'Capture A/D Once';
    csSendSN:  rec.FMethod := 'Send S/N';
    csReadSN:  rec.FMethod := 'Read S/N';
    csSendParams: rec.FMethod := 'Send Params';
    csExitCal: rec.FMethod := 'Exit Calibration';
    csLcdOn:  rec.FMethod := 'LCD On';
    csLcdOff: rec.FMethod := 'LCD Off';
    csLedOn: rec.FMethod := 'LED On';
    csLedOff: rec.FMethod := 'LED Off';
    csLeakTest: rec.FMethod := 'Leak Test';
  end;
  rec.FStatus := status;
  rec.FCnt := Fcnt;
  rec.FData := FRxBuf;
  Synchronize(procedure
    begin
      UUT.FErrRec := rec;
      UUT.ProcessUUTError
    end);
end;


end.
